<template>
  <div class="component-upload-image">
    <!-- 图片上传组件 -->
    <!--
            :action="uploadImgUrl"  上传的接口地址
             -->
    <!-- :data="fileModuleObj" 上传时附带的额外参数 -->
    <!-- list-type="picture-card" 文件列表展示类型：卡片式 -->
    <!--   :on-success="handleUploadSuccess"  上传成功回调 -->
    <!-- :before-upload="handleBeforeUpload" 上传前的校验回调 -->
    <!-- 最大上传数量 -->
    <!--:on-error="handleUploadError"  上传失败回调 -->
    <!--:on-exceed="handleExceed"  超出限制回调 -->
    <!--ref="imageUploadRef"  组件引用，用于调用内部方法 -->
    <!--:before-remove="handleDelete"  删除前的回调 -->
    <!--:show-file-list="true"  是否显示文件列表 -->
    <!--:file-list="fileList"  已上传的文件列表 -->
    <!--:on-preview="handlePictureCardPreview"  预览图片回调 -->
    <!--:class="{ hide: fileList.length >= limit }">  当达到上传上限时隐藏上传按钮 -->
    <el-upload multiple
      :action="uploadImgUrl"
      :data="fileModuleObj"
      list-type="picture-card"
      :on-success="handleUploadSuccess"
      :before-upload="handleBeforeUpload"
      :limit="limit"
      :on-error="handleUploadError"
      :on-exceed="handleExceed"
      :before-remove="handleDelete"
      :show-file-list="true"
      :file-list="fileList"
      :on-preview="handlePictureCardPreview"
      :class="{ hide: fileList.length >= limit }">
      <!-- 上传按钮的图标 -->
      <el-icon class="avatar-uploader-icon">
        <Plus />
      </el-icon>
    </el-upload>

    <!-- 上传提示信息 -->
    <div class="el-upload__tip"
         v-if="showTip">
      请上传
      <template v-if="fileSize">
        大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
      </template>
      <template v-if="fileType">
        格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
      </template>
      的文件
    </div>

    <!-- 图片预览弹窗 -->
    <el-dialog v-model="dialogVisible"
               title="预览"
               width="800px"
               append-to-body>
      <img :src="dialogImageUrl"
           style="display: block; max-width: 100%; margin: 0 auto"
           alt="" />
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { Plus } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import type { UploadInstance, UploadProps, UploadFile } from 'element-plus'

// 定义文件项的接口
interface FileItem {
  name: string // 文件名
  url: string // 文件URL
}

// 定义组件的Props接口
interface Props {
  modelValue: string | object | string[] // v-model绑定值
  limit?: number // 最大上传数量
  fileSize?: number // 文件大小限制(MB)
  fileType?: string[] // 允许的文件类型
  isShowTip?: boolean // 是否显示提示信息
  fileModule?: string // 文件所属模块
}

// Props 定义及默认值
const props = withDefaults(defineProps<Props>(), {
  limit: 5,
  fileSize: 10,
  fileType: () => ['png', 'jpg', 'jpeg'],
  isShowTip: true,
  fileModule: '用户',
})

// 定义emit事件
const emit = defineEmits<{
  'update:modelValue': [value: string] // v-model更新事件
}>()

// 组件内部状态
const imageUploadRef = ref<UploadInstance>() // 上传组件实例引用
const number = ref(0) // 当前上传数量计数
const fileModuleObj = ref({ fileModule: props.fileModule }) // 上传时的额外参数
const uploadList = ref<FileItem[]>([]) // 新上传的文件列表
const dialogImageUrl = ref('') // 预览图片的URL
const dialogVisible = ref(false) // 预览弹窗显示状态
const baseUrl = '/api' // API基础路径
const uploadImgUrl = ref(`${baseUrl}/admin/file/upload`) // 上传接口地址
const fileList = ref<FileItem[]>([]) // 已上传的文件列表

// 计算属性：是否显示提示信息
const showTip = computed(
  () => props.isShowTip && (props.fileType || props.fileSize)
)

// 监听modelValue变化，更新文件列表
watch(
  () => props.modelValue,
  (val) => {
    if (val) {
      const list = Array.isArray(val) ? val : String(val).split(',')
      fileList.value = list.map((item) => {
        if (typeof item === 'string') {
          console.log("111"+item)
          const url = item.includes(baseUrl) ? item : `${baseUrl}/${item}`
          console.log("22"+url)
          console.log("33"+item)
          return { name: item, url }
        }
        return item as FileItem
      })
    } else {
      fileList.value = []
    }
  },
  { deep: true, immediate: true }
)

// 监听fileModule变化，更新上传参数
watch(
  () => props.fileModule,
  (val) => {
    if (val) {
      fileModuleObj.value = { fileModule: val }
    }
  },
  { deep: true, immediate: true }
)

// 上传前的校验
const handleBeforeUpload: UploadProps['beforeUpload'] = (file) => {
  // 文件类型检查
  const isValidType = props.fileType.some((type) => {
    const fileExtension = file.name.split('.').pop()?.toLowerCase() || ''
    return file.type.includes(type) || fileExtension === type
  })

  if (!isValidType) {
    ElMessage.error(
      `文件格式不正确, 请上传 ${props.fileType.join('/')} 格式文件!`
    )
    return false
  }

  // 文件大小检查
  if (props.fileSize) {
    const isLt = file.size / 1024 / 1024 < props.fileSize
    if (!isLt) {
      ElMessage.error(`文件大小不能超过 ${props.fileSize}MB!`)
      return false
    }
  }

  number.value++
  return true
}

// 超出数量限制的处理
const handleExceed = () => {
  ElMessage.error(`上传文件数量不能超过 ${props.limit} 个!`)
}

// 上传成功的处理
const handleUploadSuccess: UploadProps['onSuccess'] = (res: any, file) => {
  if (res.code === 200) {
    uploadList.value.push({ name: res.data.url, url: res.data.name })
    uploadedSuccessfully()
  } else {
    number.value--
    ElMessage.error(res.msg)
    imageUploadRef.value?.handleRemove(file)
    uploadedSuccessfully()
  }
}

// 删除文件的处理
const handleDelete = (file: UploadFile) => {
  const findex = fileList.value.findIndex((f) => f.name === file.name)
  if (findex > -1 && uploadList.value.length === number.value) {
    fileList.value.splice(findex, 1)
    emit('update:modelValue', listToString(fileList.value))
    return false
  }
  return true
}

// 上传完成的处理
const uploadedSuccessfully = () => {
  if (number.value > 0 && uploadList.value.length === number.value) {
    fileList.value = [
      ...fileList.value.filter((f) => f.url !== undefined),
      ...uploadList.value,
    ]
    uploadList.value = []
    number.value = 0
    emit('update:modelValue', listToString(fileList.value))
    console.log(listToString(fileList.value))
  }
}

// 上传失败的处理
const handleUploadError = () => {
  ElMessage.error('上传图片失败')
}

// 预览图片
const handlePictureCardPreview = (file: UploadFile) => {
  dialogImageUrl.value = file.url!
  dialogVisible.value = true
}

// 将文件列表转换为字符串
const listToString = (list: FileItem[], separator: string = ',') => {
  return list
    .filter((item) => item.url && !item.url.startsWith('blob:'))
    .map((item) => item.url)
    .join(separator)
}
</script>

<style scoped lang="scss">
.component-upload-image {
  // 当达到上传限制时隐藏上传按钮
  :deep(.hide .el-upload--picture-card) {
    display: none;
  }

  // 设置上传按钮的大小
  :deep(.el-upload--picture-card) {
    width: 150px;
    height: 150px;
    line-height: 150px;
  }

  // 上传图标的样式
  .avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 150px;
    height: 150px;
    text-align: center;
  }

  // 提示文字的样式
  .el-upload__tip {
    margin-top: 8px;
    font-size: 12px;
    color: #606266;
  }
}
</style>
